// 跨房搬运

import { arriveTo, goTo } from '@/modules/move'
import { WhiteList } from '@/setting'
import { getCreepWorking } from '@/utils/common'

export const crossCarrierFn = (creep: Creep) => {
    if (creep.memory.boost && !creep.memory.boost.done) {
        return
    }

    const {
        roomName,
        targetShard,
        resourceTypes = {},
        sourceRoomName = creep.memory.belong,
        drop,
    } = creep.memory.data as CrossCarryMemory

    if (creep.memory.data?.heal === undefined) {
        creep.memory.data!.heal = creep.body.some((part) => part.type === HEAL)
    }

    // 是否有 heal 部件
    const hasHeal = creep.memory.data?.heal
    // 是否是跨 shard
    const isCrossShard = !!targetShard

    getCreepWorking(creep)

    // 周围有攻击性的敌人，或者在别人的房间就治疗自己
    if (hasHeal) {
        if (creep.hits < creep.hitsMax) {
            creep.heal(creep)
        } else if (creep.room.controller?.owner && !WhiteList.has(creep.room.controller.owner.username)) {
            creep.heal(creep)
        } else {
            const hostile = creep.pos
                .findInRange(FIND_HOSTILE_CREEPS, 3)
                .find(
                    (c) =>
                        !WhiteList.has(c.owner.username) &&
                        c.body.some(({ type }) => type === ATTACK || type === RANGED_ATTACK),
                )
            if (hostile) {
                creep.heal(creep)
            }
        }
    }

    if (creep.memory.working) {
        if (drop) {
            creep.drop(Object.keys(creep.store)[0] as ResourceConstant)
            return
        }

        if (isCrossShard) {
            if (creep.room.name !== roomName || Game.shard.name !== targetShard) {
                arriveTo(creep, new RoomPosition(25, 25, roomName), targetShard, 10)
                return
            }
        } else {
            if (creep.room.name !== roomName) {
                goTo(creep, new RoomPosition(25, 25, roomName), { range: 23 })
                return
            }
        }

        if (!creep.memory.targetId) {
            if (creep.room.storage && creep.room.storage.store.getFreeCapacity() > 30000) {
                creep.memory.targetId = creep.room.storage.id
            } else if (creep.room.terminal && creep.room.terminal.store.getFreeCapacity() > 30000) {
                creep.memory.targetId = creep.room.terminal.id
            } else {
                creep.memory.slack = 11
                return
            }
        }

        const target = Game.getObjectById(creep.memory.targetId as Id<StructureStorage | StructureTerminal>)

        if (!target || target.store.getFreeCapacity() <= 30000) {
            delete creep.memory.targetId
            return
        }

        if (creep.pos.getRangeTo(target) > 1) {
            goTo(creep, target.pos, { maxRooms: 1 })
            return
        }

        if (!creep.memory.travelTime) {
            creep.memory.travelTime = 1500 - creep.ticksToLive!
        }

        const result = creep.transfer(target, Object.keys(creep.store)[0] as ResourceConstant)

        if (
            result === OK &&
            creep.memory.travelTime &&
            creep.ticksToLive! < creep.memory.travelTime * 2 + 50 &&
            !drop
        ) {
            creep.suicide()
        }
    } else {
        // 跨 shard 暂时只运一趟
        if (isCrossShard && Game.shard.name === targetShard && creep.room.name === roomName) {
            creep.suicide()
            return
        }

        if (creep.room.name !== sourceRoomName) {
            goTo(creep, new RoomPosition(25, 25, sourceRoomName), { range: 23 })
            return
        }

        const resourceType = Object.entries(resourceTypes).find(
            ([type, number]) => creep.store[type as ResourceConstant] < number,
        )?.[0] as ResourceConstant | undefined

        if (!resourceType && !drop) {
            creep.memory.working = true
            return
        }

        if (!creep.memory.sourceId) {
            if (creep.room.storage && creep.room.storage.store.getUsedCapacity(resourceType) > 0) {
                creep.memory.sourceId = creep.room.storage.id
            } else if (creep.room.terminal && creep.room.terminal.store.getUsedCapacity(resourceType) > 0) {
                creep.memory.sourceId = creep.room.terminal.id
            } else {
                if (creep.store.getUsedCapacity() > 0) {
                    creep.memory.working = true
                } else {
                    creep.memory.slack = 11
                }
                return
            }
        }

        const source = Game.getObjectById(creep.memory.sourceId as Id<StructureStorage | StructureTerminal>)
        if (!source) {
            delete creep.memory.sourceId
            return
        }

        const findTypes = () => {
            const resSet = new Set([''])
            const keys = Object.keys(source.store)
            return keys.filter((key) => !resSet.has(key)) as ResourceConstant[]
        }
        const types = drop ? findTypes() : (Object.keys(source.store) as ResourceConstant[])
        let type = resourceType ? resourceType : (types[0] as ResourceConstant)
        if (drop) {
            // 丢弃状态先丢其他的，最后丢能量
            if (type === 'energy' && types.length >= 2) {
                type = types[1] as ResourceConstant
            }

            // 留一点
            if (type === 'energy' && source.store.getUsedCapacity(type) < 20000) {
                delete creep.memory.sourceId
                return
            }
        }

        if (source.store.getUsedCapacity(type) <= 0) {
            delete creep.memory.sourceId
            return
        }

        if (creep.pos.getRangeTo(source) > 1) {
            creep.moveTo(source.pos, { maxRooms: 2 })
            // goTo(creep, source.pos, { maxRooms: 2 })
            return
        }

        creep.withdraw(source, type)
    }
}

export interface CrossCarryMemory {
    /**
     * 目标房间
     */
    roomName: string
    /**
     * 目标 shard
     */
    targetShard?: string
    /**
     * 源房间
     */
    sourceRoomName: string
    /**
     * 资源类型
     */
    resourceTypes?: {
        [type: string]: number
    }
    /**
     * 是否直接丢弃
     */
    drop?: boolean
    /**
     * 仓库位置
     */
    sourcePos?: string
    /**
     * 是否带有 heal 部件
     */
    heal?: boolean
}
